package com.craftworks.tiles.impl;

import com.craftworks.tiles.AbstractTile;

import java.awt.*;
import java.awt.geom.Point2D;
import java.util.List;

public class GradientRingTile extends AbstractTile
{
    private double ring;
    private Color[] colors;
    private FontMetrics fmDigits;
    private FontMetrics fmSign;

    private static final Color[] DEFAULT_COLORS = {
        Color.decode("#ff2b59"),
        Color.decode("#ffa600"),
        Color.decode("#009be8"),
        Color.decode("#ff2b59")
    };

    private static final Font FONT_DIGITS = new Font("Inter Variable Text", Font.PLAIN, 24);
    private static final Font FONT_SIGN = new Font("Inter Variable Text", Font.PLAIN, 12);

    public GradientRingTile()
    {
        this(1.0);
    }

    public GradientRingTile(double value)
    {
        super(Size.LANDSCAPE);
        ring = value;
        colors = DEFAULT_COLORS;
    }

    @Override
    public List<String> propertyKeys()
    {
        return List.of("ring.value", "ring.colors");
    }

    @Override
    public void setProperty(String key, Object value)
    {
        switch (key)
        {
            case "ring.value" ->
            {
                switch (value)
                {
                    case null -> ring = 1.0;
                    case Number n -> ring = Math.max(0, Math.min(1, n.doubleValue()));
                    case String s -> ring = Math.max(0, Math.min(1, Double.parseDouble(s)));
                    default -> throw new IllegalStateException("Unexpected value: " + value);
                }
            }
            case "ring.colors" ->
            {
                switch (value)
                {
                    case null -> colors = DEFAULT_COLORS;
                    case Color[] array ->
                    {
                        if (array.length < 3 || array.length > 6)
                            throw new IllegalStateException("Unexpected value: " + value);
                        Color[] tmp = new Color[array.length + 1];
                        System.arraycopy(array, 0, tmp, 0, array.length);
                        tmp[array.length] = array[0];
                        colors = tmp;
                    }
                    case List<?> list ->
                    {
                        if (list.size() < 3 || list.size() > 6)
                            throw new IllegalStateException("Unexpected value: " + value);
                        for (Object o : list)
                            if (!(o instanceof Color c))
                                throw new IllegalStateException("Unexpected value: " + value);
                        Color[] tmp = new Color[list.size() + 1];
                        for (int i = 0; i < list.size(); i++)
                            tmp[i] = (Color) list.get(i);
                        tmp[list.size()] = (Color) list.getFirst();
                        colors = tmp;
                    }
                    default -> throw new IllegalStateException("Unexpected value: " + value);
                }
            }
            default ->
            {
                return;
            }
        }
        repaint();
    }

    @Override
    protected void paintComponent(Graphics g)
    {
        super.paintComponent(g);

        Graphics2D g2d = (Graphics2D) g;
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);

        Stroke stroke = new BasicStroke(12, BasicStroke.CAP_ROUND, BasicStroke.JOIN_MITER);
        g2d.setStroke(stroke);

        int cx = size.width / 2;
        int cy = size.height / 2;

        int radius = 36;
        int segments = colors.length - 1;
        double step = 1.0 / segments;
        double stepRadius = 2 * Math.PI * step;
        int stepAngle = (int) (360 * step);
        int startAngle = 90;
        for (int i = 0; i < segments; i++)
        {
            double theta1 = i * stepRadius + Math.PI / 2;
            double theta2 = theta1 + stepRadius;
            Point2D.Double p1 = new Point2D.Double(cx + radius * Math.cos(theta1), cy - radius * Math.sin(theta1));
            Point2D.Double p2 = new Point2D.Double(cx + radius * Math.cos(theta2), cy - radius * Math.sin(theta2));
            GradientPaint gradient = new GradientPaint(p1, colors[i], p2, colors[i + 1]);
            g2d.setPaint(gradient);

            if (ring > (i + 1) * step)
            {
                g2d.drawArc(cx - radius, cy - radius, 2 * radius, 2 * radius, startAngle, stepAngle);
                startAngle += stepAngle;
            } else
            {
                g2d.drawArc(cx - radius, cy - radius, 2 * radius, 2 * radius, startAngle, (int) ((ring - step * i) * 360));
                break;
            }
        }

        if (fmDigits == null)
            fmDigits = g2d.getFontMetrics(FONT_DIGITS);
        if (fmSign == null)
            fmSign = g2d.getFontMetrics(FONT_SIGN);
        String digits = String.format("%d", (int) (ring * 100));
        int width1 = fmDigits.stringWidth(digits);
        int width2 = fmSign.stringWidth("%");
        int width = width1 + 2 + width2;

        g2d.setPaint(FOREGROUND);
        g2d.setFont(FONT_DIGITS);
        g2d.drawString(digits, cx - width / 2 + 1, cy + 7);
        g2d.setFont(FONT_SIGN);
        g2d.drawString("%", cx + width / 2 - width2 + 1, cy + 7);
    }
}
